package com.jichangxiu.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.jichangxiu.common.annotation.Tenant;
import com.jichangxiu.common.constant.Constants;
import com.jichangxiu.common.entity.bo.ValidateBos;
import com.jichangxiu.common.exception.ServiceException;
import com.jichangxiu.common.utils.ConvertUtils;
import com.jichangxiu.common.utils.EncodeUtils;
import com.jichangxiu.common.utils.RsaUtils;
import com.jichangxiu.framework.service.impl.BaseServiceImpl;
import com.jichangxiu.framework.utils.*;
import com.jichangxiu.system.entity.bo.SysTenantBo;
import com.jichangxiu.system.entity.dto.SysTenantMenuDto;
import com.jichangxiu.system.entity.dto.add.AddSysTenant;
import com.jichangxiu.system.entity.dto.edit.EditSysTenant;
import com.jichangxiu.system.entity.dto.query.QuerySysTenant;
import com.jichangxiu.system.entity.po.*;
import com.jichangxiu.system.entity.vo.SysTenantVo;
import com.jichangxiu.system.mapper.SysTenantMapper;
import com.jichangxiu.system.service.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * SysTenantServiceImpl:
 *
 * @author JiChangXiu
 * @create 2024-06-28 13:44:49
 */
@Tenant
@Service
public class SysTenantServiceImpl extends BaseServiceImpl<SysTenantMapper, SysTenant, SysTenantVo> implements SysTenantService {

    @Resource
    private SysTenantMenuService sysTenantMenuService;

    @Resource
    private SysMenuService sysMenuService;

    @Resource
    private SysSourceService sysSourceService;

    @Resource
    private SysDeptService sysDeptService;

    @Resource
    private SysPostService sysPostService;

    @Resource
    private SysRoleService sysRoleService;

    @Resource
    private SysUserService sysUserService;

    @Resource
    private SysUserPostService sysUserPostService;

    @Resource
    private SysUserRoleService sysUserRoleService;

    /**
     * 新增系统租户
     *
     * @param addSysTenant 新增系统租户视图对象
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(AddSysTenant addSysTenant) {
        // 获取数据源
        SysSource sysSource = sysSourceService.getById(addSysTenant.getSourceId());
        if (ObjectUtil.isEmpty(sysSource))
            throw new ServiceException("未设置有效数据源");
        if (!DbUtils.checkConnection(sysSource.getType(), sysSource.getUrl(), sysSource.getUsername(), sysSource.getPassword()))
            throw new ServiceException("数据源连接失败");
        SysTenant sysTenant = addAndEditValidBo(ConvertUtils.convert(addSysTenant, SysTenantBo.class));
        sysTenant.setId(IdWorker.getIdStr());
        boolean b1 = getBaseMapper().insert(sysTenant) > 0;
        boolean b2 = true;
        if (ObjectUtil.isNotEmpty(addSysTenant.getMenuIdList())) {
            List<SysMenu> sysMenuList = sysMenuService.list(new LambdaQueryWrapper<SysMenu>().in(SysMenu::getId, addSysTenant.getMenuIdList()));
            if (ObjectUtil.isNotEmpty(sysMenuList)) {
                sysMenuList.forEach(sysMenu -> {
                    String noTenantMenuCodes = Constants.JCX_PROPERTIES.getMenuProperties().getNoTenantMenuCodes();
                    if (StrUtil.isNotBlank(noTenantMenuCodes) && ListUtil.toList(noTenantMenuCodes.split(",")).contains(sysMenu.getCode()))
                        throw new ServiceException("菜单【" + sysMenu.getName() + "】不能分配给租户");
                });
                List<SysTenantMenu> addSysTenantMenuList = new ArrayList<>();
                for (String menuId : addSysTenant.getMenuIdList()) {
                    SysMenu sysMenu = sysMenuList.stream().filter(menu -> menuId.equals(menu.getId())).findFirst().orElse(null);
                    if (ObjectUtil.isNotEmpty(sysMenu))
                        addSysTenantMenuList.add(SysTenantMenu.builder().id(IdWorker.getIdStr()).tenantId(sysTenant.getId()).menuId(menuId).type(sysMenu.getType()).startDate(sysTenant.getStartDate()).endDate(sysTenant.getEndDate()).build());
                }
                b2 = sysTenantMenuService.saveBatch(addSysTenantMenuList);
            }
        }
        // 切换为租户数据源
        DynamicUtils.changeSource(sysSource.getCode());
        // 将 部门 | 岗位 | 角色 | 用户 的数据存入租户数据源
        SysDept sysDept = null;
        if (ObjectUtil.isNotEmpty(addSysTenant.getAddSysDept())) {
            sysDept = ConvertUtils.convert(addSysTenant.getAddSysDept(), SysDept.class);
            if (ObjectUtil.isNotNull(sysDept)) {
                sysDept.setId(IdWorker.getIdStr());
                sysDept.setNo(NoUtils.createNo(sysTenant.getId(), "dept"));
                sysDept.setTenantId(sysTenant.getId());
            }
        }
        SysPost sysPost = null;
        if (ObjectUtil.isNotEmpty(addSysTenant.getAddSysPost())) {
            sysPost = ConvertUtils.convert(addSysTenant.getAddSysPost(), SysPost.class);
            if (ObjectUtil.isNotNull(sysPost)) {
                sysPost.setId(IdWorker.getIdStr());
                sysPost.setNo(NoUtils.createNo(sysTenant.getId(), "post"));
                sysPost.setTenantId(sysTenant.getId());
            }
        }
        SysRole sysRole = null;
        if (ObjectUtil.isNotEmpty(addSysTenant.getAddSysRole())) {
            sysRole = ConvertUtils.convert(addSysTenant.getAddSysRole(), SysRole.class);
            if (ObjectUtil.isNotNull(sysRole)) {
                sysRole.setId(IdWorker.getIdStr());
                sysRole.setNo(NoUtils.createNo(sysTenant.getId(), "role"));
                sysRole.setTenantId(sysTenant.getId());
            }
        }
        SysUser sysUser = null;
        if (ObjectUtil.isNotEmpty(addSysTenant.getAddSysUser())) {
            sysUser = ConvertUtils.convert(addSysTenant.getAddSysUser(), SysUser.class);
            if (ObjectUtil.isNotNull(sysUser)) {
                sysUser.setId(IdWorker.getIdStr());
                sysUser.setNo(NoUtils.createNo(sysTenant.getId(), "user"));
                sysUser.setTenantId(sysTenant.getId());
                sysUser.setPassword(EncodeUtils.bcEncode(Constants.JCX_PROPERTIES.getSecurityProperties().getLoginPasswordRsaEnabled() ? RsaUtils.decrypt(sysUser.getPassword()) : sysUser.getPassword()));
                if (ObjectUtil.isNotNull(sysDept)) {
                    sysDept.setUserId(sysUser.getId());
                    sysUser.setDeptId(sysDept.getId());
                }
            }
        }
        SysUserPost sysUserPost = null;
        if (ObjectUtil.isNotNull(sysUser) && ObjectUtil.isNotNull(sysPost)) {
            sysUserPost = SysUserPost.builder().id(IdWorker.getIdStr()).userId(sysUser.getId()).postId(sysPost.getId()).tenantId(sysTenant.getId()).build();
        }
        SysUserRole sysUserRole = null;
        if (ObjectUtil.isNotNull(sysUser) && ObjectUtil.isNotNull(sysRole)) {
            sysUserRole = SysUserRole.builder().id(IdWorker.getIdStr()).userId(sysUser.getId()).roleId(sysRole.getId()).tenantId(sysTenant.getId()).build();
        }
        boolean b3 = true;
        if (ObjectUtil.isNotEmpty(sysDept))
            b3 = sysDeptService.save(sysDept);
        boolean b4 = true;
        if (ObjectUtil.isNotEmpty(sysPost))
            b4 = sysPostService.save(sysPost);
        boolean b5 = true;
        if (ObjectUtil.isNotEmpty(sysRole))
            b5 = sysRoleService.save(sysRole);
        boolean b6 = true;
        if (ObjectUtil.isNotEmpty(sysUser))
            b6 = sysUserService.save(sysUser);
        boolean b7 = true;
        if (ObjectUtil.isNotEmpty(sysUserPost))
            b7 = sysUserPostService.save(sysUserPost);
        boolean b8 = true;
        if (ObjectUtil.isNotEmpty(sysUserRole))
            b8 = sysUserRoleService.save(sysUserRole);
        // 清空租户数据源
        DynamicUtils.resetSource();
        return b1 && b2 && b3 && b4 && b5 && b6 && b7 && b8;
    }

    /**
     * 单（多）选删除系统租户
     *
     * @param idList 系统租户主键列表
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean del(List<String> idList) {
        boolean b1 = getBaseMapper().deleteBatchIds(delValid(idList)) > 0;
        boolean b2 = sysTenantMenuService.remove(new LambdaQueryWrapper<SysTenantMenu>().in(SysTenantMenu::getTenantId, idList));
        return b1 && b2;
    }

    /**
     * 修改系统租户
     *
     * @param editSysTenant 修改系统租户视图对象
     * @return 是否成功
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean edit(EditSysTenant editSysTenant) {
        boolean b1 = getBaseMapper().updateById(addAndEditValidBo(ConvertUtils.convert(editSysTenant, SysTenantBo.class))) > 0;
        List<SysTenantMenu> sysTenantMenuList = sysTenantMenuService.list(new LambdaQueryWrapper<SysTenantMenu>().eq(SysTenantMenu::getTenantId, editSysTenant.getId()));
        boolean b2 = true;
        if (!sysTenantMenuList.stream().map(SysTenantMenu::getMenuId).collect(Collectors.toList()).equals(editSysTenant.getMenuIdList()))
            b2 = sysTenantMenuService.addOrDelOrEdit(editSysTenant, ConvertUtils.convert(sysTenantMenuList, SysTenantMenuDto.class));
        return b1 && b2;
    }

    /**
     * 根据主键查询系统租户
     *
     * @param id       系统租户主键
     * @param isExtend 是否扩展
     * @return 系统租户详情
     */
    @Override
    public SysTenantVo info(@NotBlank(message = "主键不能为空") String id, Boolean isExtend) {
        SysTenantVo sysTenantVo = getBaseMapper().selectJoinOne(SysTenantVo.class, buildQueryWrapper(QuerySysTenant.builder().id(id).build()));
        if (Boolean.TRUE.equals(isExtend) && ObjectUtil.isNotEmpty(sysTenantVo))
            sysTenantVo = renderSysTenantVoList(ListUtil.toList(sysTenantVo)).get(0);
        return sysTenantVo;
    }

    /**
     * （可分页）查询系统租户列表
     *
     * @param querySysTenant 查询视图对象
     * @return （分页）系统租户列表
     */
    @Override
    public Page<SysTenantVo> lists(QuerySysTenant querySysTenant) {
        Page<SysTenantVo> sysTenantVoPage = getBaseMapper().selectJoinPage(startPage(), SysTenantVo.class, buildQueryWrapper(querySysTenant));
        if (Boolean.TRUE.equals(querySysTenant.getIsExtend()))
            sysTenantVoPage.setRecords(renderSysTenantVoList(sysTenantVoPage.getRecords()));
        return sysTenantVoPage;
    }

    /**
     * 新增 / 修改 前校验
     *
     * @param sysTenantBo 校验用业务对象
     * @return 数据库操作对象
     */
    private SysTenant addAndEditValidBo(SysTenantBo sysTenantBo) {
        SysTenant sysTenant = ConvertUtils.convert(sysTenantBo, SysTenant.class);
        if (ObjectUtil.isNull(sysTenant))
            throw new ServiceException("待校验对象为空");
        if (!Validator.isEmail(sysTenant.getEmail()))
            throw new ServiceException("邮箱不符合规范");
        if (!Validator.isMobile(sysTenant.getPhone()))
            throw new ServiceException("手机号不符合规范");
        if (StrUtil.isEmpty(sysTenant.getSourceId()))
            throw new ServiceException("未关联数据源");
        List<ValidateBos> validateBosList = Stream.of(
                ValidateUtils.createNeValidateBos(ValidateUtils.createValidateBo(SysTenant::getId, sysTenant.getId(), "主键")),
                ValidateUtils.createOrEqValidateBos(
                        ValidateUtils.createValidateBo(SysTenant::getNo, sysTenant.getNo(), "编码"),
                        ValidateUtils.createValidateBo(SysTenant::getCode, sysTenant.getCode(), "标识"),
                        ValidateUtils.createValidateBo(SysTenant::getName, sysTenant.getName(), "名称"),
                        ValidateUtils.createValidateBo(SysTenant::getPhone, sysTenant.getPhone(), "联系电话"),
                        ValidateUtils.createValidateBo(SysTenant::getEmail, sysTenant.getEmail(), "邮箱"),
                        ValidateUtils.createValidateBo(SysTenant::getRemark, sysTenant.getRemark(), "备注"))
        ).collect(Collectors.toList());
        // 唯一校验
        ValidateUtils.uniqueValidate(this::countWrapper, validateBosList);
        if (StrUtil.isEmpty(sysTenant.getId())) {
            // 新增
            if (Constants.TEN_SUPER_TENANT.equals(sysTenant.getCode()))
                throw new ServiceException("标识不能为 " + Constants.TEN_SUPER_TENANT);
            sysTenant.setNo(NoUtils.createNo(SecurityUtils.getTenantId(), "tenant"));
            return sysTenant;
        } else {
            // 修改
            SysTenant dbSysTenant = getById(sysTenant.getId());
            if (Constants.TEN_SUPER_TENANT.equals(sysTenant.getCode()) && !sysTenant.getCode().equals(dbSysTenant.getCode()))
                throw new ServiceException("标识不能为 " + Constants.TEN_SUPER_TENANT);
            if (!dbSysTenant.getEnabled().equals(sysTenant.getEnabled()) && Constants.TEN_SUPER_TENANT.equals(sysTenant.getCode()))
                throw new ServiceException("标识为 " + Constants.TEN_SUPER_TENANT + " 的租户状态不能修改");
            if (!sysTenant.getSourceId().equals(dbSysTenant.getSourceId()) && Constants.TEN_SUPER_TENANT.equals(sysTenant.getCode()))
                throw new ServiceException("标识为 " + Constants.TEN_SUPER_TENANT + "的租户不能修改数据源");
            BeanUtil.copyProperties(sysTenant, dbSysTenant, true);
            return dbSysTenant;
        }
    }

    /**
     * 删除前校验
     *
     * @param idList 主键列表
     * @return 主键列表
     */
    private List<String> delValid(List<String> idList) {
        if (ObjectUtil.isEmpty(idList))
            throw new ServiceException("待校验主键列表为空");
        List<SysTenantBo> sysTenantBoList = selectJoinList(SysTenantBo.class, buildQueryWrapper(QuerySysTenant.builder().idList(idList).build()));
        sysTenantBoList.forEach(sysTenantBo -> {
            if (Constants.TEN_SUPER_TENANT.equals(sysTenantBo.getCode()))
                throw new ServiceException("标识为 " + Constants.TEN_SUPER_TENANT + " 的租户不能删除");
        });
        return idList;
    }

    /**
     * 渲染视图
     *
     * @param sysTenantVoList 视图列表
     * @return 渲染后的视图列表
     */
    private List<SysTenantVo> renderSysTenantVoList(List<SysTenantVo> sysTenantVoList) {
        if (ObjectUtil.isNotEmpty(sysTenantVoList)) {
            // todo 扩展信息填入
            List<SysTenantMenu> sysTenantMenuList = sysTenantMenuService.list(new LambdaQueryWrapper<SysTenantMenu>().in(SysTenantMenu::getTenantId, sysTenantVoList.stream().map(SysTenantVo::getId).collect(Collectors.toList())));
            sysTenantVoList.forEach(sysTenantVo -> sysTenantVo.setMenuIdList(sysTenantMenuList.stream().filter(sysTenantMenu -> sysTenantVo.getId().equals(sysTenantMenu.getTenantId())).map(SysTenantMenu::getMenuId).collect(Collectors.toList())));
        }
        return sysTenantVoList;
    }

    /**
     * 构建查询条件
     *
     * @param querySysTenant 查询用业务对象
     * @return 查询条件
     */
    private MPJLambdaWrapper<SysTenant> buildQueryWrapper(QuerySysTenant querySysTenant) {
        MPJLambdaWrapper<SysTenant> lambdaQueryWrapper = new MPJLambdaWrapper<SysTenant>().selectAll(SysTenant.class)
                .leftJoin(SysUser.class, SysUser::getId, SysTenant::getUserId, ext -> ext
                        .selectAs(SysUser::getName, SysTenantVo::getUserName))
                .leftJoin(SysSource.class, SysSource::getId, SysTenant::getSourceId, ext -> ext
                        .selectAs(SysSource::getCode, SysTenantVo::getSourceCode))
                .disableSubLogicDel();
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysTenant.getId()), SysTenant::getId, querySysTenant.getId());
        lambdaQueryWrapper.in(ObjectUtil.isNotEmpty(querySysTenant.getIdList()), SysTenant::getId, querySysTenant.getIdList());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysTenant.getNo()), SysTenant::getNo, querySysTenant.getNo());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysTenant.getCode()), SysTenant::getCode, querySysTenant.getCode());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysTenant.getName()), SysTenant::getName, querySysTenant.getName());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysTenant.getPhone()), SysTenant::getPhone, querySysTenant.getPhone());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysTenant.getEmail()), SysTenant::getEmail, querySysTenant.getEmail());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysTenant.getLogo()), SysTenant::getLogo, querySysTenant.getLogo());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysTenant.getPlatform()), SysTenant::getPlatform, querySysTenant.getPlatform());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysTenant.getUserId()), SysTenant::getUserId, querySysTenant.getUserId());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysTenant.getSourceId()), SysTenant::getSourceId, querySysTenant.getSourceId());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysTenant.getStartDate()), SysTenant::getStartDate, querySysTenant.getStartDate());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysTenant.getEndDate()), SysTenant::getEndDate, querySysTenant.getEndDate());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysTenant.getSort()), SysTenant::getSort, querySysTenant.getSort());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysTenant.getRemark()), SysTenant::getRemark, querySysTenant.getRemark());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysTenant.getEnabled()), SysTenant::getEnabled, querySysTenant.getEnabled());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysTenant.getEnabledBy()), SysTenant::getEnabledBy, querySysTenant.getEnabledBy());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysTenant.getEnabledTime()), SysTenant::getEnabledTime, querySysTenant.getEnabledTime());
        lambdaQueryWrapper.apply(StrUtil.isNotEmpty(querySysTenant.getScope()), querySysTenant.getScope());
        lambdaQueryWrapper.orderByAsc(SysTenant::getSort);
        return lambdaQueryWrapper;
    }

}